package server;

import java.io.IOException;
import java.net.Socket;
import java.util.Vector;



/**
 *
 * @author Muhammad Asad Ullah
 */
public class Server {



    private connectionListener listener; //Connection Listener object
    private Vector<clientHandler> myClients; //Vector to store client handlers.
    private DBFunctions dbfunctions; //Instance of DBFunctions class.
    private int chId; //(Internal)Each client handler is assigned an id, chId is starting number.
    /**
     * Initilized are the data members and starts the listener instance.
     * @param uri - Data base uri
     * @param userName - Data base user name
     * @param PassWord - Data base password.
     */
    public Server(String uri,String userName,String PassWord)
    {
        try
        {
        chId = 1;
        dbfunctions = new DBFunctions(uri,userName,PassWord);
        myClients = new Vector<clientHandler>();       
        listener = new connectionListener(this);
        }catch(IOException ioe)
        {}
    }
    /**
     *
     * @param socket -Socket is passed by connectionListerner's instance to Server which is used to
     * initlize a client handlers.
     * @return True upon success and false other wise.
     */
    public synchronized Boolean addNewClient(Socket socket)
    {
        clientHandler h = null;
        try
        {
         h = new clientHandler(this,socket,++chId);
        }
        catch(IOException ioe)
        {

        }
        myClients.add(h);       
        
        System.out.println("Total Clients = "+ myClients.size());
        return false;

    }
    /**
     * Fuction sends String passed to all the clients.
     * @param response -Response String to be send to all the users
     *
     */
    public synchronized void sendtoAll(String response)
    {

        for(int i = 0; i < myClients.size();i++)
        {
            if(myClients.elementAt(i).getUserName().equalsIgnoreCase("")) continue;
           myClients.elementAt(i).send(response);
        }
    }
    /**
     *
     * @param name - Client name to whom response should be sent
     * @param response - Response in String format (to be sent)
     */
    public synchronized void sendto(String name,String response)
    {
        for(int i = 0; i < myClients.size();i++)
        {
            if(myClients.elementAt(i).getUserName().equalsIgnoreCase(name))
            {myClients.elementAt(i).send(response);break;}
        }
    }
    /**
     *
     * @return comma seperated User list (Client list)
     */
    public synchronized String getUserList()
    {
        String list ="%%Broadcast=userlist%%";
        for(int i = 0;i < this.myClients.size();i++)
        {
            if(myClients.elementAt(i).getUserName().compareTo("") != 0)
            list += myClients.get(i).getUserName()+",";
        }
        list = list.substring(0, list.length()-1);
        return list;
    }

   /**
    * To kick a user out of chat.
    * @param parameters - parameters regarding user to be kicked
    * @return True if user is kicked successfully and false other wise.
    */
    public synchronized Boolean kickUser(String [] parameters)
    {
        int id = -1;
        for(int i = 0; i < myClients.size();i++)
        {
            if(myClients.elementAt(i).getUserName().equalsIgnoreCase(parameters[0]))
                id = myClients.elementAt(i).getClientHandlerId();
        }
        return this.removeUser(parameters[0], id, "%%Broadcast=kicked%%username="+parameters[0]);
    }
    /**
     *  To add a new user (Client)
     * @param parameters - parameters are access credentials to be stored in data base
     * @return true if Operation is successful and false other wise.
     */
    public synchronized Boolean addUser(String [] parameters)
    {
        return this.dbfunctions.addUser(parameters[0], parameters[1], (parameters[2]));
    }
    /**
     * To edit user's (Client's) access credentials and role
     * @param parameters - Client's name and new credentials
     * @return True if Operation is successfu and false otherwise.
     */
    public synchronized Boolean editUser(String [] parameters)
    {
        if( this.dbfunctions.editUser(parameters[0],parameters[1], parameters[2], (parameters[3])))
        { return true;                   }
        return false;
    }
    /**
     * To Authenticate if a user is allowed to connect to this Server
     * @param parameters - User's (Client's) Access credentials.
     * @return true if client credentials are valid and false other wise.
     */
    public synchronized Boolean AuthenticateUser(String [] parameters)
    {
        return this.dbfunctions.Authenticate(parameters[0],parameters[1],parameters[2]);
    }
   /**
    * Helper function to remove a user from chat.
    * @param name
    * @param id
    * @param message
    * @return
    */
    public synchronized Boolean removeUser(String name,int id,String message)
    {
        for(int i = 0; i < this.myClients.size();i++)
        {
            if((myClients.elementAt(i).getClientHandlerId() == id) )
            {
                myClients.elementAt(i).send(message);
                myClients.elementAt(i).setKeepRunning(Boolean.FALSE);
                myClients.remove(i).disPose() ;
                System.out.println("Total Clients = "+myClients.size());
                this.sendtoAll(this.getUserList());
                return true;
            }

        }
        return false;
        
    }
    /**
     *  To delete a user and blocking him from reconnecting to this Server
     * @param parameters - Client's name
     * @return True if Operatin is Successful and false otherwise.
     */
    public synchronized Boolean deleteUser(String [] parameters)
    {
        if(parameters == null || parameters[0] == null) return false;
        if( this.dbfunctions.deleteUser(parameters[0]))
        {
            for(int i = 0; i < myClients.size();i++)
            {
                if(myClients.elementAt(i).getUserName().compareToIgnoreCase(parameters[0]) == 0)
                {
                    myClients.elementAt(i).setKeepRunning(false);
                    myClients.elementAt(i).send("%%Broadcast=deleted%%username="+parameters[0]);
                    myClients.elementAt(i).send("%%Broadcast=kicked%%username="+parameters[0]);
                    myClients.remove(i).disPose();
                    this.sendtoAll(this.getUserList());
                    return true;
                }
                return true;
            }
        }
        return false;
    }

    /**
     * To Start a new instance of this Server.
     * @param args - provide in the following order
     * Database uri for example -- //"jdbc:sqlserver://localhost:2301;";
     * Data base access user-name
     * Data base Access password
     */
    public static void main(String [] args)
    {
       if(args.length < 3) {System.out.println("Please provide all required arguments");System.exit(1);}
       String uri = args[0];//"jdbc:sqlserver://localhost:2301;";
       String username = args[1] ; //"sa";
       String password = args[2]; //"bool";
        Server s = new Server(uri,username,password);
    }
}


